索引函數的高階應用

林嶔 (Lin, Chin)

Lesson 5

第一節:資料轉換概念(1)

– 當然,之後也許你會看到一些函數能加速整個流程,但在面對小筆資料時熟練的使用迴圈能幫忙我們迅速做完資料轉換

– 這份資料是從三軍總醫院生化檢驗值系統截取某10位病患在這段期間內所測得之各式生化值

dat = read.csv("data3_3.csv", header = TRUE, fileEncoding = 'CP950')
head(dat, 10)
##    PATNUMBER          COLLECTIONDATE      TESTNAME RESVALUE
## 1       2185 2011/12/12 上午 8:09:00    Creatinine      7.0
## 2       2185 2011/12/12 上午 8:09:00 Total Calcium      7.1
## 3       2185 2011/12/12 上午 8:09:00            Na    137.0
## 4       2185 2011/12/12 上午 8:09:00            IP      7.9
## 5        691 2011/12/12 下午 6:32:00    Creatinine      3.1
## 6       2185 2011/12/29 上午 6:19:00    Creatinine      7.2
## 7       2185 2011/12/29 上午 6:19:00            Na    136.0
## 8        691 2011/12/19 上午 4:38:00    Creatinine      8.0
## 9        691 2011/12/19 上午 4:38:00            Na    137.0
## 10      2185 2011/12/19 上午 8:47:00    Creatinine      8.1
##      PATNUMBER COLLECTIONDATE            Albumin Albumin body fluid AST
## [1,] "175"     "2011/10/1 上午 8:24:00"  NA      NA                 NA 
## [2,] "175"     "2011/10/5 下午 4:46:00"  NA      NA                 NA 
## [3,] "175"     "2011/10/6 上午 9:01:00"  NA      NA                 NA 
## [4,] "175"     "2011/10/8 上午 6:42:00"  NA      NA                 NA 
## [5,] "175"     "2011/11/10 上午 9:01:00" NA      NA                 NA 
## [6,] "175"     "2011/11/10 下午 1:25:00" NA      NA                 NA 
##      BUN BUN Fluid Cholesterol Fluid Creatinine Creatinine Fluid GLU(AC)
## [1,] NA  NA        NA                "3.7"      NA               NA     
## [2,] NA  NA        NA                "3.2"      NA               NA     
## [3,] NA  NA        NA                NA         NA               NA     
## [4,] NA  NA        NA                "3.4"      NA               NA     
## [5,] NA  NA        NA                NA         NA               NA     
## [6,] NA  NA        NA                NA         NA               NA     
##      HDL-Cholesterol IP    K  LDL-Cholesterol Na    Total Calcium
## [1,] NA              "4.3" NA NA              "138" "7.3"        
## [2,] NA              NA    NA NA              "139" NA           
## [3,] NA              "4.5" NA NA              NA    "7.8"        
## [4,] NA              NA    NA NA              NA    NA           
## [5,] NA              "4.5" NA NA              NA    "7.3"        
## [6,] NA              NA    NA NA              NA    NA           
##      Total Cholesterol Triglyceride Triglycerol Fluid Uric Acid
## [1,] NA                NA           NA                NA       
## [2,] NA                NA           NA                NA       
## [3,] "342"             "335"        NA                NA       
## [4,] NA                NA           NA                NA       
## [5,] "342"             "326"        NA                NA       
## [6,] NA                NA           NA                NA       
##      urine Calcium urine Phosphorus urine Potassium urine Sodium
## [1,] NA            NA               NA              NA          
## [2,] NA            NA               NA              NA          
## [3,] NA            NA               NA              NA          
## [4,] NA            NA               NA              NA          
## [5,] NA            NA               NA              NA          
## [6,] "0.6"         "28.3"           "39.1"          "48"        
##      urine Uric Acid
## [1,] NA             
## [2,] NA             
## [3,] NA             
## [4,] NA             
## [5,] NA             
## [6,] NA

第一節:資料轉換概念(2)

– 一樣,先取得這份資料的基本資訊

levels.TESTNAME = levels(dat[,3])
levels.TESTNAME
##  [1] "Albumin"            "Albumin body fluid" "AST"               
##  [4] "BUN"                "BUN Fluid"          "Cholesterol Fluid" 
##  [7] "Creatinine"         "Creatinine Fluid"   "GLU(AC)"           
## [10] "HDL-Cholesterol"    "IP"                 "K"                 
## [13] "LDL-Cholesterol"    "Na"                 "Total Calcium"     
## [16] "Total Cholesterol"  "Triglyceride"       "Triglycerol Fluid" 
## [19] "Uric Acid"          "urine Calcium"      "urine Phosphorus"  
## [22] "urine Potassium"    "urine Sodium"       "urine Uric Acid"
n.TESTNAME = length(levels.TESTNAME)
n.TESTNAME
## [1] 24
levels.PATNUMBER = levels(as.factor(dat[,1]))
levels.PATNUMBER
##  [1] "175"  "356"  "691"  "1332" "1350" "1654" "1826" "2074" "2154" "2185"
n.PATNUMBER = length(levels.PATNUMBER)
n.PATNUMBER
## [1] 10

第一節:資料轉換概念(3)

– 在寫迴圈時,我習慣先在起頭令迴圈變數為1,如果這段以後能執行,那應該整個迴圈都不會有問題

i = 1
subdat = dat[dat[,1]==levels.PATNUMBER[i],]
levels.COLLECTIONDATE = levels(subdat[,2])
n.COLLECTIONDATE = length(levels.COLLECTIONDATE)
n.COLLECTIONDATE
## [1] 1532
subdat[,2] = as.factor(as.character(subdat[,2]))
levels.COLLECTIONDATE = levels(subdat[,2])
n.COLLECTIONDATE = length(levels.COLLECTIONDATE)
n.COLLECTIONDATE
## [1] 132

第一節:資料轉換概念(4)

– 第一欄填ID,第二欄填上這個人所有測量的時間點

submatrix = matrix(NA, nrow = n.COLLECTIONDATE, ncol = n.TESTNAME+2)
colnames(submatrix) = c("PATNUMBER", "COLLECTIONDATE", levels.TESTNAME)

submatrix[,1] = levels.PATNUMBER[i]
submatrix[,2] = levels.COLLECTIONDATE

head(submatrix)
##      PATNUMBER COLLECTIONDATE            Albumin Albumin body fluid AST
## [1,] "175"     "2011/10/1 上午 8:24:00"  NA      NA                 NA 
## [2,] "175"     "2011/10/5 下午 4:46:00"  NA      NA                 NA 
## [3,] "175"     "2011/10/6 上午 9:01:00"  NA      NA                 NA 
## [4,] "175"     "2011/10/8 上午 6:42:00"  NA      NA                 NA 
## [5,] "175"     "2011/11/10 上午 9:01:00" NA      NA                 NA 
## [6,] "175"     "2011/11/10 下午 1:25:00" NA      NA                 NA 
##      BUN BUN Fluid Cholesterol Fluid Creatinine Creatinine Fluid GLU(AC)
## [1,] NA  NA        NA                NA         NA               NA     
## [2,] NA  NA        NA                NA         NA               NA     
## [3,] NA  NA        NA                NA         NA               NA     
## [4,] NA  NA        NA                NA         NA               NA     
## [5,] NA  NA        NA                NA         NA               NA     
## [6,] NA  NA        NA                NA         NA               NA     
##      HDL-Cholesterol IP K  LDL-Cholesterol Na Total Calcium
## [1,] NA              NA NA NA              NA NA           
## [2,] NA              NA NA NA              NA NA           
## [3,] NA              NA NA NA              NA NA           
## [4,] NA              NA NA NA              NA NA           
## [5,] NA              NA NA NA              NA NA           
## [6,] NA              NA NA NA              NA NA           
##      Total Cholesterol Triglyceride Triglycerol Fluid Uric Acid
## [1,] NA                NA           NA                NA       
## [2,] NA                NA           NA                NA       
## [3,] NA                NA           NA                NA       
## [4,] NA                NA           NA                NA       
## [5,] NA                NA           NA                NA       
## [6,] NA                NA           NA                NA       
##      urine Calcium urine Phosphorus urine Potassium urine Sodium
## [1,] NA            NA               NA              NA          
## [2,] NA            NA               NA              NA          
## [3,] NA            NA               NA              NA          
## [4,] NA            NA               NA              NA          
## [5,] NA            NA               NA              NA          
## [6,] NA            NA               NA              NA          
##      urine Uric Acid
## [1,] NA             
## [2,] NA             
## [3,] NA             
## [4,] NA             
## [5,] NA             
## [6,] NA

– 同樣的,我們先看第一個時間點,注意迴圈變數不要重複,所以剛剛設i,這次我們設j

j = 1
subsubdat = subdat[subdat[,2]==levels.COLLECTIONDATE[j],]
subsubdat
##      PATNUMBER         COLLECTIONDATE      TESTNAME RESVALUE
## 3993       175 2011/10/1 上午 8:24:00            IP      4.3
## 3994       175 2011/10/1 上午 8:24:00 Total Calcium      7.3
## 3995       175 2011/10/1 上午 8:24:00    Creatinine      3.7
## 3996       175 2011/10/1 上午 8:24:00            Na    138.0

第一節:資料轉換概念(5)

  1. 找出這列是描述哪個生化值
  2. 找出在submatrix中這個生化值的位置
  3. 把value填入那個位置

– 函數「which()」可以幫我們找位置

k = 1
NAME = subsubdat[k,3]
NAME
## [1] IP
## 24 Levels: Albumin Albumin body fluid AST BUN ... urine Uric Acid
position = which(NAME == levels.TESTNAME) + 2 
position
## [1] 13
submatrix[j, position] = subsubdat[k,4]
head(submatrix)
##      PATNUMBER COLLECTIONDATE            Albumin Albumin body fluid AST
## [1,] "175"     "2011/10/1 上午 8:24:00"  NA      NA                 NA 
## [2,] "175"     "2011/10/5 下午 4:46:00"  NA      NA                 NA 
## [3,] "175"     "2011/10/6 上午 9:01:00"  NA      NA                 NA 
## [4,] "175"     "2011/10/8 上午 6:42:00"  NA      NA                 NA 
## [5,] "175"     "2011/11/10 上午 9:01:00" NA      NA                 NA 
## [6,] "175"     "2011/11/10 下午 1:25:00" NA      NA                 NA 
##      BUN BUN Fluid Cholesterol Fluid Creatinine Creatinine Fluid GLU(AC)
## [1,] NA  NA        NA                NA         NA               NA     
## [2,] NA  NA        NA                NA         NA               NA     
## [3,] NA  NA        NA                NA         NA               NA     
## [4,] NA  NA        NA                NA         NA               NA     
## [5,] NA  NA        NA                NA         NA               NA     
## [6,] NA  NA        NA                NA         NA               NA     
##      HDL-Cholesterol IP    K  LDL-Cholesterol Na Total Calcium
## [1,] NA              "4.3" NA NA              NA NA           
## [2,] NA              NA    NA NA              NA NA           
## [3,] NA              NA    NA NA              NA NA           
## [4,] NA              NA    NA NA              NA NA           
## [5,] NA              NA    NA NA              NA NA           
## [6,] NA              NA    NA NA              NA NA           
##      Total Cholesterol Triglyceride Triglycerol Fluid Uric Acid
## [1,] NA                NA           NA                NA       
## [2,] NA                NA           NA                NA       
## [3,] NA                NA           NA                NA       
## [4,] NA                NA           NA                NA       
## [5,] NA                NA           NA                NA       
## [6,] NA                NA           NA                NA       
##      urine Calcium urine Phosphorus urine Potassium urine Sodium
## [1,] NA            NA               NA              NA          
## [2,] NA            NA               NA              NA          
## [3,] NA            NA               NA              NA          
## [4,] NA            NA               NA              NA          
## [5,] NA            NA               NA              NA          
## [6,] NA            NA               NA              NA          
##      urine Uric Acid
## [1,] NA             
## [2,] NA             
## [3,] NA             
## [4,] NA             
## [5,] NA             
## [6,] NA

第一節:資料轉換概念(6)

subsubdat #先看看subsubdat裡面有哪些
##      PATNUMBER         COLLECTIONDATE      TESTNAME RESVALUE
## 3993       175 2011/10/1 上午 8:24:00            IP      4.3
## 3994       175 2011/10/1 上午 8:24:00 Total Calcium      7.3
## 3995       175 2011/10/1 上午 8:24:00    Creatinine      3.7
## 3996       175 2011/10/1 上午 8:24:00            Na    138.0
for (k in 1:nrow(subsubdat)) {
  NAME = subsubdat[k,3]
  position = which(NAME == levels.TESTNAME) + 2
  submatrix[j, position] = subsubdat[k,4]
}
head(submatrix)
##      PATNUMBER COLLECTIONDATE            Albumin Albumin body fluid AST
## [1,] "175"     "2011/10/1 上午 8:24:00"  NA      NA                 NA 
## [2,] "175"     "2011/10/5 下午 4:46:00"  NA      NA                 NA 
## [3,] "175"     "2011/10/6 上午 9:01:00"  NA      NA                 NA 
## [4,] "175"     "2011/10/8 上午 6:42:00"  NA      NA                 NA 
## [5,] "175"     "2011/11/10 上午 9:01:00" NA      NA                 NA 
## [6,] "175"     "2011/11/10 下午 1:25:00" NA      NA                 NA 
##      BUN BUN Fluid Cholesterol Fluid Creatinine Creatinine Fluid GLU(AC)
## [1,] NA  NA        NA                "3.7"      NA               NA     
## [2,] NA  NA        NA                NA         NA               NA     
## [3,] NA  NA        NA                NA         NA               NA     
## [4,] NA  NA        NA                NA         NA               NA     
## [5,] NA  NA        NA                NA         NA               NA     
## [6,] NA  NA        NA                NA         NA               NA     
##      HDL-Cholesterol IP    K  LDL-Cholesterol Na    Total Calcium
## [1,] NA              "4.3" NA NA              "138" "7.3"        
## [2,] NA              NA    NA NA              NA    NA           
## [3,] NA              NA    NA NA              NA    NA           
## [4,] NA              NA    NA NA              NA    NA           
## [5,] NA              NA    NA NA              NA    NA           
## [6,] NA              NA    NA NA              NA    NA           
##      Total Cholesterol Triglyceride Triglycerol Fluid Uric Acid
## [1,] NA                NA           NA                NA       
## [2,] NA                NA           NA                NA       
## [3,] NA                NA           NA                NA       
## [4,] NA                NA           NA                NA       
## [5,] NA                NA           NA                NA       
## [6,] NA                NA           NA                NA       
##      urine Calcium urine Phosphorus urine Potassium urine Sodium
## [1,] NA            NA               NA              NA          
## [2,] NA            NA               NA              NA          
## [3,] NA            NA               NA              NA          
## [4,] NA            NA               NA              NA          
## [5,] NA            NA               NA              NA          
## [6,] NA            NA               NA              NA          
##      urine Uric Acid
## [1,] NA             
## [2,] NA             
## [3,] NA             
## [4,] NA             
## [5,] NA             
## [6,] NA

第一節:資料轉換概念(7)

for (j in 1:n.COLLECTIONDATE) {
  subsubdat = subdat[subdat[,2]==levels.COLLECTIONDATE[j],]
  for (k in 1:nrow(subsubdat)) {
    NAME = subsubdat[k,3]
    position = which(NAME == levels.TESTNAME) + 2
    submatrix[j, position] = subsubdat[k,4]
  }
}

head(submatrix)
##      PATNUMBER COLLECTIONDATE            Albumin Albumin body fluid AST
## [1,] "175"     "2011/10/1 上午 8:24:00"  NA      NA                 NA 
## [2,] "175"     "2011/10/5 下午 4:46:00"  NA      NA                 NA 
## [3,] "175"     "2011/10/6 上午 9:01:00"  NA      NA                 NA 
## [4,] "175"     "2011/10/8 上午 6:42:00"  NA      NA                 NA 
## [5,] "175"     "2011/11/10 上午 9:01:00" NA      NA                 NA 
## [6,] "175"     "2011/11/10 下午 1:25:00" NA      NA                 NA 
##      BUN BUN Fluid Cholesterol Fluid Creatinine Creatinine Fluid GLU(AC)
## [1,] NA  NA        NA                "3.7"      NA               NA     
## [2,] NA  NA        NA                "3.2"      NA               NA     
## [3,] NA  NA        NA                NA         NA               NA     
## [4,] NA  NA        NA                "3.4"      NA               NA     
## [5,] NA  NA        NA                NA         NA               NA     
## [6,] NA  NA        NA                NA         NA               NA     
##      HDL-Cholesterol IP    K  LDL-Cholesterol Na    Total Calcium
## [1,] NA              "4.3" NA NA              "138" "7.3"        
## [2,] NA              NA    NA NA              "139" NA           
## [3,] NA              "4.5" NA NA              NA    "7.8"        
## [4,] NA              NA    NA NA              NA    NA           
## [5,] NA              "4.5" NA NA              NA    "7.3"        
## [6,] NA              NA    NA NA              NA    NA           
##      Total Cholesterol Triglyceride Triglycerol Fluid Uric Acid
## [1,] NA                NA           NA                NA       
## [2,] NA                NA           NA                NA       
## [3,] "342"             "335"        NA                NA       
## [4,] NA                NA           NA                NA       
## [5,] "342"             "326"        NA                NA       
## [6,] NA                NA           NA                NA       
##      urine Calcium urine Phosphorus urine Potassium urine Sodium
## [1,] NA            NA               NA              NA          
## [2,] NA            NA               NA              NA          
## [3,] NA            NA               NA              NA          
## [4,] NA            NA               NA              NA          
## [5,] NA            NA               NA              NA          
## [6,] "0.6"         "28.3"           "39.1"          "48"        
##      urine Uric Acid
## [1,] NA             
## [2,] NA             
## [3,] NA             
## [4,] NA             
## [5,] NA             
## [6,] NA

第一節:資料轉換概念(8)

– 下列這串程式碼可以獲得一個完整的submatrix

i = 1

subdat = dat[dat[,1]==levels.PATNUMBER[i],]
subdat[,2] = as.factor(as.character(subdat[,2]))
levels.COLLECTIONDATE = levels(subdat[,2])
n.COLLECTIONDATE = length(levels.COLLECTIONDATE)
n.COLLECTIONDATE

submatrix = matrix(NA, nrow = n.COLLECTIONDATE, ncol = n.TESTNAME+2)
colnames(submatrix) = c("PATNUMBER", "COLLECTIONDATE", levels.TESTNAME)

submatrix[,1] = levels.PATNUMBER[i]
submatrix[,2] = levels.COLLECTIONDATE

for (j in 1:n.COLLECTIONDATE) {
  subsubdat = subdat[subdat[,2]==levels.COLLECTIONDATE[j],]
  for (k in 1:nrow(subsubdat)) {
    NAME = subsubdat[k,3]
    position = which(NAME == levels.TESTNAME) + 2
    submatrix[j, position] = subsubdat[k,4]
  }
}
final.data = NULL
final.data
## NULL
final.data = rbind(final.data, submatrix)
head(final.data)
##      PATNUMBER COLLECTIONDATE            Albumin Albumin body fluid AST
## [1,] "175"     "2011/10/1 上午 8:24:00"  NA      NA                 NA 
## [2,] "175"     "2011/10/5 下午 4:46:00"  NA      NA                 NA 
## [3,] "175"     "2011/10/6 上午 9:01:00"  NA      NA                 NA 
## [4,] "175"     "2011/10/8 上午 6:42:00"  NA      NA                 NA 
## [5,] "175"     "2011/11/10 上午 9:01:00" NA      NA                 NA 
## [6,] "175"     "2011/11/10 下午 1:25:00" NA      NA                 NA 
##      BUN BUN Fluid Cholesterol Fluid Creatinine Creatinine Fluid GLU(AC)
## [1,] NA  NA        NA                "3.7"      NA               NA     
## [2,] NA  NA        NA                "3.2"      NA               NA     
## [3,] NA  NA        NA                NA         NA               NA     
## [4,] NA  NA        NA                "3.4"      NA               NA     
## [5,] NA  NA        NA                NA         NA               NA     
## [6,] NA  NA        NA                NA         NA               NA     
##      HDL-Cholesterol IP    K  LDL-Cholesterol Na    Total Calcium
## [1,] NA              "4.3" NA NA              "138" "7.3"        
## [2,] NA              NA    NA NA              "139" NA           
## [3,] NA              "4.5" NA NA              NA    "7.8"        
## [4,] NA              NA    NA NA              NA    NA           
## [5,] NA              "4.5" NA NA              NA    "7.3"        
## [6,] NA              NA    NA NA              NA    NA           
##      Total Cholesterol Triglyceride Triglycerol Fluid Uric Acid
## [1,] NA                NA           NA                NA       
## [2,] NA                NA           NA                NA       
## [3,] "342"             "335"        NA                NA       
## [4,] NA                NA           NA                NA       
## [5,] "342"             "326"        NA                NA       
## [6,] NA                NA           NA                NA       
##      urine Calcium urine Phosphorus urine Potassium urine Sodium
## [1,] NA            NA               NA              NA          
## [2,] NA            NA               NA              NA          
## [3,] NA            NA               NA              NA          
## [4,] NA            NA               NA              NA          
## [5,] NA            NA               NA              NA          
## [6,] "0.6"         "28.3"           "39.1"          "48"        
##      urine Uric Acid
## [1,] NA             
## [2,] NA             
## [3,] NA             
## [4,] NA             
## [5,] NA             
## [6,] NA

第一節:資料轉換概念(9)

levels.TESTNAME = levels(dat[,3])
n.TESTNAME = length(levels.TESTNAME)
levels.PATNUMBER = levels(as.factor(dat[,1]))
n.PATNUMBER = length(levels.PATNUMBER)

final.data = NULL

for (i in 1:n.PATNUMBER) {
  subdat = dat[dat[,1]==levels.PATNUMBER[i],]
  subdat[,2] = as.factor(as.character(subdat[,2]))
  levels.COLLECTIONDATE = levels(subdat[,2])
  n.COLLECTIONDATE = length(levels.COLLECTIONDATE)

  submatrix = matrix(NA, nrow = n.COLLECTIONDATE, ncol = n.TESTNAME+2)
  colnames(submatrix) = c("PATNUMBER", "COLLECTIONDATE", levels.TESTNAME)

  submatrix[,1] = levels.PATNUMBER[i]
  submatrix[,2] = levels.COLLECTIONDATE

  for (j in 1:n.COLLECTIONDATE) {
    subsubdat = subdat[subdat[,2]==levels.COLLECTIONDATE[j],]
    for (k in 1:nrow(subsubdat)) {
      NAME = subsubdat[k,3]
      position = which(NAME == levels.TESTNAME) + 2
      submatrix[j, position] = subsubdat[k,4]
    }
  }
  
  final.data = rbind(final.data, submatrix)
}

head(final.data)

練習1:更大的檔案

– 這次,除了檔案更大以外,檔案的最後還有參考值。如果你的值位於參考值內,那就是正常,否則則是過高。

– 我們這次不要填數值,而是填入正常(TRUE)或異常(FALSE)!

dat = read.csv("data3_4.csv", header = TRUE, fileEncoding = 'CP950')
head(dat, 10)
##    PATNUMBER SEX          COLLECTIONDATE          TESTNAME RESVALUE  UNITS
## 1        180   1 2011/12/11 上午 5:10:00                Na    131.0 mmol/L
## 2        589   1 2011/12/11 上午 6:37:00        Creatinine      3.8  mg/dL
## 3        589   1 2011/12/11 上午 6:37:00                Na    138.0 mmol/L
## 4       1015   1 2011/12/12 上午 7:38:00 Total Cholesterol    158.0  mg/dL
## 5       1015   1 2011/12/12 上午 7:38:00        Creatinine      1.5  mg/dL
## 6       1015   1 2011/12/12 上午 7:38:00      Triglyceride    140.0  mg/dL
## 7       1015   1 2011/12/12 上午 7:38:00                Na    143.0 mmol/L
## 8        480   2 2011/12/12 上午 7:41:00      Triglyceride    153.0  mg/dL
## 9        480   2 2011/12/12 上午 7:41:00                Na    139.0 mmol/L
## 10       480   2 2011/12/12 上午 7:41:00 Total Cholesterol    211.0  mg/dL
##    MINIMUM MAXIMUM
## 1    136.0   145.0
## 2      0.7     1.2
## 3    136.0   145.0
## 4       NA   200.0
## 5      0.7     1.2
## 6       NA   200.0
## 7    136.0   145.0
## 8       NA   200.0
## 9    136.0   145.0
## 10      NA   200.0

– 函數「Sys.sleep()」是讓系統休息,你不需要將他加入你的迴圈內

n = 100
pb = txtProgressBar(max = n, style=3)
for(i in 1:n) {
  Sys.sleep(0.1)
  setTxtProgressBar(pb, i)
}
close(pb)

練習1答案

levels.TESTNAME = levels(dat[,'TESTNAME'])
n.TESTNAME = length(levels.TESTNAME)
levels.PATNUMBER = levels(as.factor(dat[,'PATNUMBER']))
n.PATNUMBER = length(levels.PATNUMBER)

final.data = NULL

pb = txtProgressBar(max = n.PATNUMBER, style=3)

for (i in 1:n.PATNUMBER) {
  subdat = dat[dat[,'PATNUMBER']==levels.PATNUMBER[i],]
  subdat[,'COLLECTIONDATE'] = as.factor(as.character(subdat[,'COLLECTIONDATE']))
  levels.COLLECTIONDATE = levels(subdat[,'COLLECTIONDATE'])
  n.COLLECTIONDATE = length(levels.COLLECTIONDATE)

  submatrix = matrix(NA, nrow = n.COLLECTIONDATE, ncol = n.TESTNAME+2)
  colnames(submatrix) = c("PATNUMBER", "COLLECTIONDATE", levels.TESTNAME)

  submatrix[,1] = levels.PATNUMBER[i]
  submatrix[,2] = levels.COLLECTIONDATE

  for (j in 1:n.COLLECTIONDATE) {
    subsubdat = subdat[subdat[,'COLLECTIONDATE']==levels.COLLECTIONDATE[j],]
    for (k in 1:nrow(subsubdat)) {
      NAME = subsubdat[k,'TESTNAME']
      position = which(NAME == levels.TESTNAME) + 2
      VALUE = subsubdat[k,'RESVALUE']
      MINIMUM = subsubdat[k,'MINIMUM']
      MAXIMUM = subsubdat[k,'MAXIMUM']
      if (is.na(MINIMUM)) {MINIMUM = -Inf}
      if (is.na(MAXIMUM)) {MAXIMUM = Inf}
      submatrix[j, position] = (VALUE >= MINIMUM & VALUE <= MAXIMUM)
    }
  }
  
  final.data = rbind(final.data, submatrix)
  
  setTxtProgressBar(pb, i)
  
}

close(pb)

head(final.data)

第二節:列表(List)層物件基本介紹(1)

– 如果你有注意到的話,迴圈運行速度其實是越來越慢,這個問題其實是出在我們的函數「rbind」,這個函數雖然能夠方便的把兩個資料表合併成一個,但他的過程其實對記憶體很不友善的!

– 列表(List)層分為列表(list)、S3物件(S3 class)及S4物件(S4 class):

  1. 列表(list):在R裡面,向量的上層是陣列層物件。若是我們希望在一個物件內放置很多陣列層物件,我們會用到列表。値得一提的是,列表裡面可以同時包含數個陣列層物件及變數層物件。

  2. S3物件(S3 class):S3物件是一種特殊的列表物件,他的變化會在後面慢慢介紹。

  3. S4物件(S4 class):S4物件與前面兩種有非常大的不同,相關的函數也不一樣,在本節課我們不會教到。

# 先產生一個數値矩陣物件
x1 = 1:20
M1 = matrix(x1, nrow = 4, ncol = 5)
M1
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    5    9   13   17
## [2,]    2    6   10   14   18
## [3,]    3    7   11   15   19
## [4,]    4    8   12   16   20
# 再產生一個文字矩陣物件
x2 = c("A", "B", "C", "A", "C", "B", "B", "B", "A")
M2 = matrix(x2, nrow = 3, ncol = 3)
M2
##      [,1] [,2] [,3]
## [1,] "A"  "A"  "B" 
## [2,] "B"  "C"  "B" 
## [3,] "C"  "B"  "A"
# 再產生一個邏輯向量
x3 = c(TRUE, FALSE, TRUE, FALSE)
x3
## [1]  TRUE FALSE  TRUE FALSE
# 將上述這些物件打包成一個列表物件
L1 = list(M1, M2, x3)
L1
## [[1]]
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    5    9   13   17
## [2,]    2    6   10   14   18
## [3,]    3    7   11   15   19
## [4,]    4    8   12   16   20
## 
## [[2]]
##      [,1] [,2] [,3]
## [1,] "A"  "A"  "B" 
## [2,] "B"  "C"  "B" 
## [3,] "C"  "B"  "A" 
## 
## [[3]]
## [1]  TRUE FALSE  TRUE FALSE

第二節:列表(List)層物件基本介紹(2)

  1. 函數「length()」可以協助我們了解物件長度

  2. 函數「class()」可以查詢該物件的屬性

  3. 函數「names()」可以協助我們命名物件

  4. 函數「ls()」可以協助我們看看物件中有哪些東西

length(L1)
## [1] 3
class(L1)
## [1] "list"
names(L1) = c("A", "B", "C")
L1
## $A
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    5    9   13   17
## [2,]    2    6   10   14   18
## [3,]    3    7   11   15   19
## [4,]    4    8   12   16   20
## 
## $B
##      [,1] [,2] [,3]
## [1,] "A"  "A"  "B" 
## [2,] "B"  "C"  "B" 
## [3,] "C"  "B"  "A" 
## 
## $C
## [1]  TRUE FALSE  TRUE FALSE
ls(L1)
## [1] "A" "B" "C"

第二節:列表(List)層物件基本介紹(3)

L1[[2]]
##      [,1] [,2] [,3]
## [1,] "A"  "A"  "B" 
## [2,] "B"  "C"  "B" 
## [3,] "C"  "B"  "A"
L1[["B"]]
##      [,1] [,2] [,3]
## [1,] "A"  "A"  "B" 
## [2,] "B"  "C"  "B" 
## [3,] "C"  "B"  "A"
L1$B
##      [,1] [,2] [,3]
## [1,] "A"  "A"  "B" 
## [2,] "B"  "C"  "B" 
## [3,] "C"  "B"  "A"
L1[[2]][2,3]
## [1] "B"
L1[["B"]][3,1]
## [1] "C"
L1$B[1,2]
## [1] "A"

第二節:列表(List)層物件基本介紹(4)

#先看看L1的樣子
L1
## $A
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    5    9   13   17
## [2,]    2    6   10   14   18
## [3,]    3    7   11   15   19
## [4,]    4    8   12   16   20
## 
## $B
##      [,1] [,2] [,3]
## [1,] "A"  "A"  "B" 
## [2,] "B"  "C"  "B" 
## [3,] "C"  "B"  "A" 
## 
## $C
## [1]  TRUE FALSE  TRUE FALSE
#先看看L1的物件屬性
class(L1)
## [1] "list"
#強迫L1成為別的物件屬性
class(L1) = "test"
#再看看L1的物件屬性
class(L1)
## [1] "test"
#看看L1現在的樣子
L1
## $A
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    5    9   13   17
## [2,]    2    6   10   14   18
## [3,]    3    7   11   15   19
## [4,]    4    8   12   16   20
## 
## $B
##      [,1] [,2] [,3]
## [1,] "A"  "A"  "B" 
## [2,] "B"  "C"  "B" 
## [3,] "C"  "B"  "A" 
## 
## $C
## [1]  TRUE FALSE  TRUE FALSE
## 
## attr(,"class")
## [1] "test"

– 小提示:當你使用函數「class()」可以查詢該物件的屬性,若非常見的幾種屬性名稱,那就非常有可能是S3物件(S3 class)或S4物件(S4 class)

第二節:列表(List)層物件基本介紹(5)

#先寫一個自訂函數「print.test()」
print.test = function(test) {
  cat("此列表共有",length(test),"個物件\n")
  cat("物件名稱分別為:\n")
  cat(paste(names(test), collapse = ", "), "\n")
}

#再看看請R列印出L1會變什麼
L1
## 此列表共有 3 個物件
## 物件名稱分別為:
## A, B, C

– 列表(list)的幾個常見函數還是能夠使用:

ls(L1)
## [1] "A" "B" "C"
length(L1)
## [1] 3
class(L1)
## [1] "test"
names(L1) = c("D", "E", "F")
L1
## 此列表共有 3 個物件
## 物件名稱分別為:
## D, E, F

第二節:列表(List)層物件基本介紹(6)

– 在寫之前我們先看看直接對L1使用函數「summary()」會怎樣

summary(L1)
##   Length Class  Mode     
## D 20     -none- numeric  
## E  9     -none- character
## F  4     -none- logical

– 現在我們可以讓函數「summary()」使用後產生不同的結果

#先寫一個自訂函數「summary.test()」
summary.test = function(test) {
  cat("此列表共有",length(test),"個物件\n")
  cat("物件名稱分別為:\n")
  cat(paste(names(test), collapse = ", "), "\n")
  for (i in 1:length(test)) {
    cat(names(test)[i], "之物件屬性為", class(test[[i]]), "\n")
  }
}

#再看看使用函數「summary()」後會變什麼
summary(L1)
## 此列表共有 3 個物件
## 物件名稱分別為:
## D, E, F 
## D 之物件屬性為 matrix 
## E 之物件屬性為 matrix 
## F 之物件屬性為 logical

練習2:善用S3物件格式

Test_list = list(student = c('小明', '小華', '小愛'),
                 score = c(80, 90, 75))

Test_list
## $student
## [1] "小明" "小華" "小愛"
## 
## $score
## [1] 80 90 75
Test_list
## 小明 的分數為 80 
## 小華 的分數為 90 
## 小愛 的分數為 75

練習2答案

  1. 指定一個物件名稱

  2. 編寫特定的「print」函數

Test_list = list(student = c('小明', '小華', '小愛'),
                 score = c(80, 90, 75))

class(Test_list) = 'My_list'

print.My_list = function(Test_list) {
  for (i in 1:length(Test_list[[1]])) {
    cat(Test_list[[1]][i], "的分數為", Test_list[[2]][i], "\n")
  }
}

Test_list
## 小明 的分數為 80 
## 小華 的分數為 90 
## 小愛 的分數為 75

第三節:用列表特性加速任務(1)

– 讓我們做個小測試,假設我們不斷的將一個完全相同的資料表用rbind指令合併,和先使用列表儲存,看看時間差異有多大:

– 這是連續合併1000次的耗時:

t0 = Sys.time()

base_dat = data.frame(X = rnorm(20), Y = rnorm(20))
final_dat = NULL

for (i in 1:1000) {
  
  final_dat = rbind(final_dat, base_dat)
  
}

Sys.time() - t0 #用現在時間減去開始時間
## Time difference of 0.3139393 secs

– 這是連續合併2000次的耗時:

t0 = Sys.time()

base_dat = data.frame(X = rnorm(20), Y = rnorm(20))
final_dat = NULL

for (i in 1:2000) {
  
  final_dat = rbind(final_dat, base_dat)
  
}

Sys.time() - t0 #用現在時間減去開始時間
## Time difference of 1.036624 secs

– 這是連續合併4000次的耗時:

t0 = Sys.time()

base_dat = data.frame(X = rnorm(20), Y = rnorm(20))
final_dat = NULL

for (i in 1:4000) {
  
  final_dat = rbind(final_dat, base_dat)
  
}

Sys.time() - t0 #用現在時間減去開始時間
## Time difference of 3.934517 secs

第三節:用列表特性加速任務(2)

– 這是連續合併1000次的耗時:

t0 = Sys.time()

base_dat = data.frame(X = rnorm(20), Y = rnorm(20))
dat_list = list()

for (i in 1:1000) {
  
  dat_list[[i]] = base_dat
  
}

Sys.time() - t0 #用現在時間減去開始時間
## Time difference of 0.006249428 secs

– 這是連續合併2000次的耗時:

t0 = Sys.time()

base_dat = data.frame(X = rnorm(20), Y = rnorm(20))
dat_list = list()

for (i in 1:2000) {
  
  dat_list[[i]] = base_dat
  
}

Sys.time() - t0 #用現在時間減去開始時間
## Time difference of 0.006228924 secs

– 這是連續合併4000次的耗時:

t0 = Sys.time()

base_dat = data.frame(X = rnorm(20), Y = rnorm(20))
dat_list = list()

for (i in 1:4000) {
  
  dat_list[[i]] = base_dat
  
}

Sys.time() - t0 #用現在時間減去開始時間
## Time difference of 0.006935596 secs

第三節:用列表特性加速任務(3)

– 但這裡需要用到函數「do.call」,他的用法是把list裡面的物件當作參數:

dat1 = read.csv("data3_3.csv", header = TRUE, fileEncoding = 'CP950')
param_list = list(file = "data3_3.csv", header = TRUE, fileEncoding = 'CP950')
dat2 = do.call("read.csv", param_list)
all.equal(dat1, dat2)
## [1] TRUE

– 函數「rbind」與函數「do.call」的搭配將能完成任務:

t0 = Sys.time()

base_dat = data.frame(X = rnorm(20), Y = rnorm(20))
dat_list = list()

for (i in 1:4000) {
  
  dat_list[[i]] = base_dat
  
}

my_data = do.call("rbind", dat_list)

Sys.time() - t0 #用現在時間減去開始時間
## Time difference of 0.1578779 secs
nrow(my_data)
## [1] 80000
head(my_data)
##              X          Y
## 1 -0.382981508  0.2002918
## 2  0.155653132  1.0562852
## 3 -0.067983361 -0.7352638
## 4  0.004562337 -0.1909763
## 5  0.204647467 -0.4511755
## 6  0.071733906  1.3465746

練習3:再做一次練習1

– 這次請你學著使用列表特性加速過程!

練習3答案

levels.TESTNAME = levels(dat[,'TESTNAME'])
n.TESTNAME = length(levels.TESTNAME)
levels.PATNUMBER = levels(as.factor(dat[,'PATNUMBER']))
n.PATNUMBER = length(levels.PATNUMBER)

final.data_list = list() # 改這裡

pb = txtProgressBar(max = n.PATNUMBER, style=3)

for (i in 1:n.PATNUMBER) {
  subdat = dat[dat[,'PATNUMBER']==levels.PATNUMBER[i],]
  subdat[,'COLLECTIONDATE'] = as.factor(as.character(subdat[,'COLLECTIONDATE']))
  levels.COLLECTIONDATE = levels(subdat[,'COLLECTIONDATE'])
  n.COLLECTIONDATE = length(levels.COLLECTIONDATE)

  submatrix = matrix(NA, nrow = n.COLLECTIONDATE, ncol = n.TESTNAME+2)
  colnames(submatrix) = c("PATNUMBER", "COLLECTIONDATE", levels.TESTNAME)

  submatrix[,1] = levels.PATNUMBER[i]
  submatrix[,2] = levels.COLLECTIONDATE

  for (j in 1:n.COLLECTIONDATE) {
    subsubdat = subdat[subdat[,'COLLECTIONDATE']==levels.COLLECTIONDATE[j],]
    for (k in 1:nrow(subsubdat)) {
      NAME = subsubdat[k,'TESTNAME']
      position = which(NAME == levels.TESTNAME) + 2
      VALUE = subsubdat[k,'RESVALUE']
      MINIMUM = subsubdat[k,'MINIMUM']
      MAXIMUM = subsubdat[k,'MAXIMUM']
      if (is.na(MINIMUM)) {MINIMUM = -Inf}
      if (is.na(MAXIMUM)) {MAXIMUM = Inf}
      submatrix[j, position] = (VALUE >= MINIMUM & VALUE <= MAXIMUM)
    }
  }
  
  final.data_list[[i]] = submatrix # 改這裡
  
  setTxtProgressBar(pb, i)
  
}

close(pb)


final.data = do.call("rbind", final.data_list) # 改這裡

head(final.data)

小結

  1. 列表僅僅是索引,因此為其增加物件並不需要額外複製的動作,因此原則上使用這個物件格式進行操作

  2. 如果一定要在資料表內完成任務,那預先創造好一個空的資料表,讓程式為其填數字較有效率